<template>
    <!-- - 特点：Vue3 中的 watch 只能监视以下四种数据：
            1. `ref` 定义的数据。常用
            2. `reactive` 定义的数据。
            3. 函数返回一个值（getter 函数）。常用
            4. 一个包含上述内容的数组。 -->
<div>
   <h1>情况一（常用）：监视【ref】定义的【基本数据类型】</h1>
   <h2>{{sum}}</h2>
   <button @click="sum++">点我sum+1</button>
   <h1>情况二：监视【ref】定义的【对象数据类型】</h1>
   <h2>{{person.name}}</h2>
   <h2>{{person.age}}</h2>
   <button @click="changeName">点我修改name</button>
   <button @click="changeAge">点我修改age</button>
   <button @click="changePerson">点我修改整个人person</button>
   <h1>情况三（常用）：监视【reactive】定义的【对象类型数据】</h1>
   <h2>{{person1.name}}</h2>
   <h2>{{person1.age}}</h2>
   <button @click="changeName1">点我修改name1</button>
   <button @click="changeAge1">点我修改age1</button>
   <button @click="changePerson1">点我修改整个人person1</button>
   <h1>情况四：监听【ref】或【reactive】定义的【对象类型】数据中的【某个属性】，getter函数封装</h1>
   <h2>姓名：{{person2.name}}</h2>
   <h2>年龄：{{person2.age}}</h2>
   <h2>奔驰：{{person2.car.Benc}}</h2>
   <h2>宝马：{{person2.car.BWM}}</h2>
   <button @click="changeName2">点我修改姓名</button>
   <button @click="changeCarBenc">点我修改奔驰</button>
   <button @click="changeCarBWM">点我修改宝马</button>
   <button @click="changeCar">点我修改所有车</button>
   <h1>情况五：监视以上所有情况，放在数组里</h1>
   <h2>姓名：{{person3.name}}</h2>
   <h2>年龄：{{person3.age}}</h2>
   <h2>奔驰：{{person3.city.Shanghai}}</h2>
   <h2>宝马：{{person3.city.Guangzhou}}</h2>
   <button @click="changeName3">点我修改姓名</button>
   <button @click="changeCityShanghai">点我修改上海</button>
   <button @click="changeCityGuangzhou">点我修改广州</button>
   <button @click="changeCity">点我修改所有城市</button>
   
</div>
</template>

<!-- 组件名字配置：单独使用script来声明组件名字，繁琐
<script lang="ts">
// 将组件Person注册为全局组件，
// 全局组件可以在任意组件中使用<person></person>，无需导入
export default {
    name: 'Person'
}
</script> -->

<!-- 配置 -->
<!-- setup作用：自动将顶层绑定（变量、函数、导入的模块，无需return）暴露到模板中（temmplate）中 ，setup在生命周期beforeCreate之前执行-->
<script setup lang="ts">
// ref定义基本类型、数组、对象类型数据，JS中使用需要.value；reactive定义对象、数组等数据为响应式，JS中使用不需要.value
import { reactive,ref,watch} from 'vue'
defineOptions({
name: "PersonComponent"
});
// /*****************************************情况一：监视【ref】定义的【基本数据类型】************************************************ */
//情况一：监视【ref】定义的【基本数据类型】
 let sum=ref(0);
 //参数是(响应式数据，回调函数):watch监听自带的，其中newValue和oldValue为监听的数据，也是必须带的
 //watch监听函数会返回一个函数，这个函数调用后会取消监听，所以可以在特定条件下停止监听
let stopWatch=watch(sum,function(newValue,oldValue){//参数是响应式数据对象，不是响应式数据的值
    console.log(`监听sum变化：新值：${newValue}，旧值：${oldValue}`);//减少字符串拼接写法
    //监听条件：必须写在函数内部
    if(sum.value>10)
    {
        stopWatch();
    }
});
// /*****************************************情况二：监视【ref】定义的【对象数据类型】************************************************ */
// 情况二：监视【ref】定义的【对象数据类型】
let person=ref({
    name:"张三",
    age:18
});
function changePerson()
{
   person.value={name:"王五",age:20};
}
function changeName()
{
    person.value.name+="~";
}
function changeAge()
{
    person.value.age+=1;
}
// 注意:
// • 若修改的是ref定义的对象中的属性，newValue 和 oldValue 都是新值，因为它们是同一个对象。
// • 若修改整个 ref 定义的对象，newValue 是新值，oldValue 是旧值，因为不是同一个对象了。
//未加深度监听：如果只是改变监听对象的属性值，其实这个对象并不会改变引用，所以监听失效
// 加深度监听：监听对象属性值变化，属性值变化了会调用监听函数，但是newValue和oldValue都是指向的这个对象，要是只改变属性，即使监听到了newValue和oldValue，因为对象没变化，所以打印出来的newValue和oldValue还是最新的属性值
//如果想监听对象的属性值变化，需要深度监听
// watch的第一个参数是：被监视的数据
// watch的第二个参数是：监视的回调
// watch的第三个参数是：配置对象（deep、immediate等等）
watch(person,(newValue,oldValue)=>{
    //字符串类型所以要格式转换，除非和字符串分开打印
    // console.log(`监听person变化：新值：${JSON.stringify(newValue)}，旧值：${JSON.stringify(oldValue)}`);
    console.log("person变化了",newValue,oldValue);
},{deep:true});//深度监听，监听对象内部属性的变化
// /****************************************情况三：监听【reactive】定义的【对象类型数据】*********************************************** */
//情况三：监听【reactive】定义的【对象类型数据】
let person1=reactive({
    name:"张三",
    age:18
});
function changePerson1()
{
    //reactive定义的对象，不能直接修改对象
//    person1={name:"王五",age:20};
//   这种方式其实也只是批量修改了对象的属性，对象的引用没有改变
    person1=Object.assign(person1,{name:"王五",age:20});

}
function changeName1()
{
    person1.name+="~";
}
function changeAge1()
{
    person1.age+=1;
}
//第三个情况的测试：三种方式都没有改变对象的引用
//情况三：监听【reactive】定义的【对象类型数据】，默认开启深度监听且无法关闭（监听对象属性值，且新旧对象并未改变，指向同一个对象，打印出的都是新值）
watch(person1,(newValue,oldValue)=>{
    console.log("person1变化了",newValue,oldValue);
});//深度监听，监听对象内部属性的变化
// /****************************************情况四：监听【ref】或【reactive】定义的【对象类型】数据中的【某个属性】*********************************************** */
//情况四：监视【ref】或【reactive】定义的【对象类型】数据中的【某个属性】，某个属性，注意点如下：
//   1. 若该属性值不是【对象类型】，需要写成函数形式。
//   2. 若该属性值是依然是【对象类型】，可直接编，也可写成函数，建议写成函数。不加depp:true，只能监听对象的改变。加了还能监听内部属性值的变化
let person2=reactive({
    name:"张三",
    age:18,
    car:{
        Benc:"奔驰",
        BWM:"宝马"
    }
});

function changeName2()
{
    person2.name+="~";
}
function changeCarBenc()
{
    person2.car.Benc="法拉利";
}
function changeCarBWM()
{
    person2.car.BWM="迈巴赫";
}
function changeCar()
{
    person2.car={Benc:"迈凯伦",BWM:"劳斯莱斯"};
}
//   1. 若该属性值不是【对象类型】，需要写成函数形式。
//   2. 若该属性值是依然是【对象类型】，可直接编，也可写成函数，建议写成函数。不加depp:true，只能监听对象的改变。加了还能监听内部属性值的变化
//   3. getter函数：一个函数，返回一个值function get(){return person2.name}====>简写为箭头函数：()=>person2.name

//属性值：基本数据类型
watch(()=>person2.name,(newValue,oldValue)=>{
    console.log("person2.name变化了",newValue,oldValue);
});
//属性值：对象数据类型,建议写成函数，不加depp:true，只能监听对象的改变。加了deepp:true，还能监听内部属性值变化
watch(()=>person2.car,(newValue,oldValue)=>{
    console.log("person2.car变化了",newValue,oldValue);
},{deep:true});
//情况五：监听以上所有情况，放在数组中
let person3=ref({
    name:"张三",
    age:18,
    city:{
        Shanghai:"上海",
        Guangzhou:"广州"
    }
});
function changeName3()
{
    person3.value.name+="~";
}
function changeCityShanghai()
{
    person3.value.city.Shanghai="北京";
}
function changeCityGuangzhou()
{
    person3.value.city.Guangzhou="深圳";
}
function changeCity()
{
    person3.value.city={Shanghai:"沈阳",Guangzhou:"杭州"};
}
watch([
    ()=>person3.value.name,
    person3.value.city
],(newValue,oldValue)=>{
    console.log("person3变化了",newValue,oldValue);
},{deep:true});
</script>

<style scoped>

.person{
    background-color: aqua;
    color: red;
    height: 400px;
}
</style>